<script setup lang="ts">
import { reactive, ref, onMounted, watch, nextTick, onUnmounted } from "vue";
import MarkdownIt from 'markdown-it'
import mdKatex from '@traptitech/markdown-it-katex'
import hljs from 'highlight.js';
import vue from 'highlight.js/lib/languages/xml';
import react from 'highlight.js/lib/languages/xml';
import xml from 'highlight.js/lib/languages/xml';
import 'highlight.js/styles/a11y-dark.css';
import { fetchEventSource } from '@microsoft/fetch-event-source';
import Clipboard from 'clipboard'
import { aicgApi } from '@/api/aicg.ts'
import { userApi } from '@/api/user.ts'
import ContextMenu from '@/components/ContextMenu.vue'
import { useRouter } from 'vue-router';
import { allInstall, categories, menuObject, promptObject, templateObject, aiModelsObject, requestObject } from '@/utils/types/ai'
import { BASE_URL } from '@/utils/http/config';
import { useStore } from '@/store/index'
import useFileUpload from '@/composables/useFileUpload'

hljs.registerLanguage('vue', vue);
hljs.registerLanguage('react', react);
hljs.registerLanguage('xml', xml);
const CONSTATNS = {
    // API相关
    API: {
        BASE_URL: BASE_URL,
        DEEPSEEK_MODULE: 'deepseekMoudle',
        CHAT_COMPLETIONS: '/api/v1/chat/completions',
        AUTH_TOKEN: 'hoXELWmslCuCNmAaHXsz:rhsXWxrwstLQBdVsWEyI'
    },
    // 模型相关
    MODELS: {
        DEEPSEEK_V3: 'deepseek-v3',
        SPARK_ULTRA: 'spark4.0-ultra',
        DEFAULT_MODULE: 'deepseek-chat',
        REASONER_MODULE: 'deepseek-reasoner'
    },
    // 消息提示
    MESSAGES: {
        COPY_SUCCESS: '复制成功',
        COPY_FAILED: '复制失败',
        INPUT_REQUIRED: '请输入文字',
        ANSWERING: '回答中，请稍后再试',
        CLEAR_DISABLED: '回答中，无法清除消息，请先停止输出',
        NO_MESSAGES: '不存在聊天消息，无法清空',
        SERVER_ERROR: '服务器繁忙，请稍后再试',
        REASONER_ONLY: '只允许deepseek v3模型使用深度思考模式',
        MODEL_SWITCH_DISABLED: '此次对话只能使用spark4.0-ultra大模型，不允许切换',
        REASONER_NOT_SUPPORTED: 'spark4.0-ultra大模型不支持深度思考',
        DOWNLOAD_SUCCESS: '下载成功',
        DOWNLOAD_FAILED: '下载失败，请重试',
        DELETE_SUCCESS: '删除成功'
    },
    // 默认配置
    DEFAULT: {
        AI_AVATAR: 'https://ai-public.mastergo.com/ai/img_res/62f8190121cf7f46c510bfc3910b9518.jpg',
        USER_AVATAR: 'https://ai-public.mastergo.com/ai/img_res/838a4ab73af1c2e4de9e11a5ce843075.jpg',
        AI_NAME: '冒险AI助手',
        PAGE_SIZE: 3,
        PROMPT_VALUE: 200
    }
}

defineOptions({
  name: "mxcontent",
});

let aiAvatarUrl = ref(CONSTATNS.DEFAULT.AI_AVATAR);
let showUserMenu = ref(false);

// 用户信息
const nickAll = reactive({
  nickname: '',
  nicknameId: 0,
  nickImg: CONSTATNS.DEFAULT.USER_AVATAR,
  appid: '',
  userNickname: ''
})
let showUserProfile = ref(false) //是否显示个人信息


const contentText = ref('') // 文字输入
let allText: allInstall[] = reactive([]) // 对话内容

let isGenerate = ref(false)
let imgCurrent = ref('')

const akiText = ref('')
const isAskText = ref(false)

const timeCount = ref(0)
const chatContainer = ref<null | HTMLElement>(null)
const textareaDialogue = ref<null | HTMLElement>(null)
const markdownContent = ref('')
const serviceThinkContent = ref('')
const isAnswering = ref(false) // 是否可以enter
const ctrl = ref<null | AbortController>(null)
const isStopText = ref(false) // 是否显示中断文字
let clipboard = ref<ClipboardJS>()
const isTyping = ref(false) //是否输入中
let categoryData: categories[] = reactive([])
const currentIndex = ref(0) // AI选中
const roleContent = ref('') // 大模型描述
const recordLength = ref(0) // 对话信息长度
const dialogTableVisible = ref(false) // 大模型选择弹窗
const revealAlways = ref({
    revealId: 0,
    revealName: CONSTATNS.DEFAULT.AI_NAME,
    revealImg: CONSTATNS.DEFAULT.AI_AVATAR
})
let promptList: promptObject[] = reactive([])
const promptIndex = ref(0)
let templateList: templateObject[] = reactive([])
const router = useRouter()
let promptValue = ref(CONSTATNS.DEFAULT.PROMPT_VALUE)
let hoveredIndex = ref<number | null>(null)
const disabled = ref(false)
let page = ref(1) // 当前页数
let pageSize = ref(CONSTATNS.DEFAULT.PAGE_SIZE) // 每页展示条目
let total = ref(0)  // 总条数
let editIndex = ref(-1)
let editText = ref('')
let currentModel = ref(CONSTATNS.MODELS.DEEPSEEK_V3);
let aiModels: aiModelsObject[] = reactive([
  { id: CONSTATNS.MODELS.DEEPSEEK_V3, name: 'DeepSeek-V3' },
  { id: CONSTATNS.MODELS.SPARK_ULTRA, name: 'Spark4.0 Ultra' }
])
let showModelMenu = ref(false)
let isDisabled = ref(true)

let fileInput = ref<null | HTMLInputElement>(null) // 文件上传input

let editAll = ref<{ img_url?: string; nickname?: string; username?: string }>({ img_url: '', nickname: '', username: '' })

// 滚动条参数
let closeScroll = ref(false)
let lastScrollPosition = ref(0)

let isDeepThinking = ref(false)
let apiModuleId = ref(CONSTATNS.MODELS.DEFAULT_MODULE)
let requestContent: requestObject[] = reactive([])
let responce = ''

let downloadClickHandler: ((e: MouseEvent) => void) | null = null;

// 文件上传参数
const parseFile = ref<null | HTMLInputElement>(null)
let filenameAll = ref({
    isFileContent: false,
    name: '',
    size: '',
    type: '',
    status: '1', // 1:上传中, 2:上传成功, 3:上传失败
    url: ''
})
let wordOrpdfText = ref('')

const { selectFile, upImg } = useFileUpload(fileInput, editAll, 1)

onMounted(() => {
    getUser()
    chatContainer.value?.addEventListener("scroll", windowScroll)

    // 添加代码块下载按钮点击事件监听
    downloadClickHandler = (e: MouseEvent) => {
        const target = e.target as HTMLElement;
        if (target.classList.contains('code-block-header__download')) {
            const encodedCode = target.getAttribute('data-code') || '';
            const lang = target.getAttribute('data-lang') || '';
            downloadCode(encodedCode, lang);
        }
    };
    document.addEventListener('click', downloadClickHandler);
})

onUnmounted(() => {
    // 移除点击事件监听器
    if (downloadClickHandler) {
        document.removeEventListener('click', downloadClickHandler);
    }
    chatContainer.value?.removeEventListener('scroll', windowScroll)
    if (clipboard.value) {
        clipboard.value.destroy();
    }
})


const windowScroll = (event: Event) => {
    const currentScroll = (event.target as HTMLElement).scrollTop
    // 判断滚动条滑动方向
    const direction = currentScroll > lastScrollPosition.value ? 'down' : 'up'
    lastScrollPosition.value = currentScroll
    if (direction == 'up') {
        closeScroll.value = true;
    }
}

// 统一消息提示
const showMessage = (message: string, type: 'success' | 'warning' | 'error' | 'info' = 'info', options: any = {}) => {
    ElMessage({
        message,
        type,
        customClass: 'ele-text',
        plain: true,
        ...options
    })
}

// 获取用户信息
const getUser = () => {
    userApi.getUserData()
        .then(res => {
            if (res.data.code == 0) {
                const { id, nickname, img_url, appid, username } = res.data.users;
                nickAll.nicknameId = id;
                nickAll.nickname = nickname;
                nickAll.nickImg = img_url;
                nickAll.appid = appid;
                nickAll.userNickname = username;
                editAll.value = res.data.users
                getClassInformation(nickAll.nicknameId)
            } else {
                showMessage(res.data.msg, 'error')
            }
        })
}

// 获取用户所有大模型分类
const getClassInformation = (id: Number) => {
    aicgApi.queryCategory({user_id: id})
        .then(res => {
            if (res.data.code == 0) {
                categoryData.splice(0, categoryData.length)
                categoryData.push(...res.data.data)
            } else {
                showMessage(res.data.msg, 'error')
            }
        })
}

// 富文本展示
const mdi = new MarkdownIt({
  linkify: true,
  highlight(code, language) {
    const validLang = !!(language && hljs.getLanguage(language))
    if (validLang) {
      const lang = language ?? ''
      return highlightBlock(hljs.highlightAuto(code, [lang]).value, lang, code)
    }
    return highlightBlock(hljs.highlightAuto(code).value, '', code)
  }
})
mdi.use(mdKatex, { blockClass: 'katexmath-block rounded-md p-[10px]', errorColor: ' #cc0000' })

// 高亮代码区域内容
const highlightBlock = <T extends string>(str: T, lang: T, word: T) => {
  // 当前时间加随机数生成唯一的id标识
  const codeIndex = Date.now() + Math.floor(Math.random() * 10000000)
  // 使用 TextEncoder 和 TextDecoder 处理特殊字符
  const encodedCode = btoa(new TextEncoder().encode(word).reduce((data, byte) => data + String.fromCharCode(byte), ''))
  return `<pre class="pre-code-box"><div class="pre-code-header"><span class="code-block-header__lang">${lang}</span><div class="code-block-header__actions"><span class="code-block-header__copy" data-clipboard-action="copy" data-clipboard-target="#copy${codeIndex}">复制代码</span><span class="code-block-header__download" data-code="${encodedCode}" data-lang="${lang}">下载代码</span></div></div><div class="pre-code"><code class="hljs code-block-body ${lang}">${str}</code></div></pre><textarea style="position: absolute;top: -9999px;left: -9999px;z-index: -9999;" id="copy${codeIndex}">${word}</textarea>`
}

// 下载代码功能
const downloadCode = (encodedCode: string, language: string) => {
  try {
    // 解码 Base64 编码的代码
    const binaryString = atob(encodedCode)
    const bytes = new Uint8Array(binaryString.length)
    for (let i = 0; i < binaryString.length; i++) {
      bytes[i] = binaryString.charCodeAt(i)
    }
    const code = new TextDecoder().decode(bytes)
    
    const blob = new Blob([code], { type: 'text/plain' });
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    let targetLanguage = ''
    if (language == 'javascript') {
        targetLanguage = 'js'
    } else if (language == 'typescript') {
        targetLanguage = 'ts'
    } else {
        targetLanguage = language
    }
    a.href = url;
    a.download = `code.${targetLanguage}`;
    document.body.appendChild(a);
    a.click();
    window.URL.revokeObjectURL(url);
    document.body.removeChild(a);
    showMessage(CONSTATNS.MESSAGES.DOWNLOAD_SUCCESS, 'success')
  } catch (error) {
    showMessage(CONSTATNS.MESSAGES.DOWNLOAD_FAILED, 'error')
  }
}

// 转化成HTML结构
const getMdiText = <T extends string>(value: T) => {
    return mdi.render(value)
}

// 退出登录
const outLogin = () => {
    ElNotification({
        title: '你的冒险之旅已结束',
        message: '欢迎再次前来冒险',
        type: 'success',
    })
    useStore().clearToken()
    router.push('/')
}

// 切换大模型api
const switchModel = (id: string) => {
  if (currentIndex.value) {
    showMessage(CONSTATNS.MESSAGES.MODEL_SWITCH_DISABLED, 'warning')
    return
  }
  if (currentModel.value !== id && apiModuleId.value == 'deepseek-reasoner') {
    showMessage(CONSTATNS.MESSAGES.REASONER_NOT_SUPPORTED, 'warning')
    return
  }
  currentModel.value = id;
  showModelMenu.value = false;
}

// 复制功能
const copyCode = (ele: string) => {
    if (clipboard.value) {
        clipboard.value.destroy();
    }
    clipboard.value = new Clipboard(ele)
    clipboard.value.on('success', function () {
        showMessage(CONSTATNS.MESSAGES.COPY_SUCCESS, 'success')
    });
    clipboard.value.on('error', function () {
        showMessage(CONSTATNS.MESSAGES.COPY_FAILED, 'error')
    });
}

// 保存对话信息
const saveMessage = () => {
    if (currentModel.value == CONSTATNS.MODELS.DEEPSEEK_V3) {
        requestContent.push({ role: "assistant", content: responce })
        return
    }
    interface transfer {
        myself_reply: string;
        ai_reply: string;
        category_id: number;
    }
    let data:Array<transfer> = []
    for (let i=recordLength.value;i<allText.length;i++) {
        let transfer = {
            myself_reply: allText[i].userText,
            ai_reply: allText[i].adminContent.adminText,
            category_id: currentIndex.value,
            user_id: nickAll.nicknameId
        }
        data.push(transfer)
    }
    aicgApi.addDialogue(data)
        .then(res=>{
            if (res.data.code == 0) {
                onceQueryDialogue(currentIndex.value, 1)
            } else {
                showMessage(res.data.msg, 'warning')
            }
        })
}

// 查询对话信息接口
const onceQueryDialogue = (id: number, index: number) => {
    aicgApi.queryDialogue({category_id: id})
        .then(res => {
            if (res.data.code == 0) {
                // 解决异步竞态问题
                if (index == 0 && !(currentIndex.value == id)) {
                    return
                }
                allText.splice(0, allText.length)
                allText.push(...res.data.data)
                timeCount.value = allText.length
                recordLength.value = allText.length
                copyCode('.code-block-header__copy')
            } else {
                showMessage(res.data.msg, 'error')
            }
        })
}

// 回车键提交、ctrl+enter组合键换行
const handleKeydown = (event: KeyboardEvent) => {
    if (event.ctrlKey && event.key == 'Enter') {
        event.preventDefault()
        const textarea = event.target as HTMLTextAreaElement
        const start = textarea.selectionStart
        const end = textarea.selectionEnd
        const value = textarea.value
        textarea.value = value.substring(0, start) + '\n' + value.substring(end)
        textarea.selectionStart = textarea.selectionEnd = start + 1
        adjustTextareaHeight(event)
    } else if (event.key == 'Enter') {
        event.preventDefault();
        sendTxt(contentText.value)
    }
}

// 调整输入框高度
const adjustTextareaHeight = (event: Event) => {
  const textarea = event.target as HTMLTextAreaElement
  textarea.style.height = 'auto'
  textarea.style.height = `${Math.min(textarea.scrollHeight, 150)}px`
}

const sendTxt = <T extends string>(value: T) => {
  if (isAnswering.value) {
        showMessage(CONSTATNS.MESSAGES.ANSWERING, 'warning')
        return
    }
    if (!value) {
        showMessage(CONSTATNS.MESSAGES.INPUT_REQUIRED, 'warning')
        return
    }
    if (filenameAll.value.isFileContent && filenameAll.value.status == '1') {
        showMessage('文件正在上传中，请稍后再试', 'warning')
        return
    } else if (filenameAll.value.isFileContent && filenameAll.value.status == '3') {
        showMessage('文件上传失败，请重新上传', 'warning')
        return
    }
    isAnswering.value = true
    contentText.value = ''
    if (textareaDialogue.value) {
        textareaDialogue.value.style.height = 'auto'
    }
    filenameAll.value.isFileContent = false
    if (currentModel.value == CONSTATNS.MODELS.DEEPSEEK_V3) {
      let messagesContent = ""
      if (akiText.value && isAskText.value) {
          messagesContent = akiText.value + '\n\n' + value
          isAskText.value = false
          akiText.value = ''
      } else {
          messagesContent = value
      }
      if (apiModuleId.value == CONSTATNS.MODELS.REASONER_MODULE) {
        allText.push({userText: messagesContent, adminContent: {adminText: "", isShow: true}, thinkContent: {thinkText: "", status: '1', isShow: true, isOpen: false}})
      } else {
        allText.push({userText: messagesContent, adminContent: {adminText: "", isShow: true}})
      }
      requestContent.push({ role: "user", content: messagesContent + wordOrpdfText.value })
      let params =  {
          "text": requestContent,
          "moduleId": apiModuleId.value
      }
      closeScroll.value = false;
      ctrl.value = new AbortController();
      fetchEventSource(CONSTATNS.API.BASE_URL + CONSTATNS.API.DEEPSEEK_MODULE, {
          method: 'POST',
          headers: {
              'Content-Type': 'application/json'
          },
          body: JSON.stringify(params),
          signal: ctrl.value.signal,
          openWhenHidden: true,
          onmessage(ev) {
              let res = JSON.parse(ev.data)
              lastScrollPosition.value = 0;
              if (res.choices && res.choices[0].delta.reasoning_content && apiModuleId.value == CONSTATNS.MODELS.REASONER_MODULE) {
                  serviceThinkContent.value += res.choices[0].delta.reasoning_content
                  allText[timeCount.value].thinkContent!.thinkText = getMdiText(serviceThinkContent.value)
                  allText[timeCount.value].thinkContent!.status = '1'
                  allText[timeCount.value].thinkContent!.isOpen = true
                  allText[timeCount.value].adminContent.isShow = false
                  isStopText.value = true
              }
              if (serviceThinkContent.value && res.choices && res.choices[0].delta.content) {
                allText[timeCount.value].thinkContent!.status = '3'
              }
              if (res.choices && res.choices[0].delta.content) {
                markdownContent.value += res.choices[0].delta.content
                responce += res.choices[0].delta.content
                allText[timeCount.value].adminContent.isShow = false
                allText[timeCount.value].adminContent.adminText = getMdiText(markdownContent.value)
                isStopText.value = true
              }
              if (res.message) {
                  isAnswering.value = false;
                  allText[timeCount.value].adminContent.isShow = false
                  allText[timeCount.value].adminContent.adminText = res.message
              }
          },
          onclose() {
              // 关闭连接
              markdownContent.value = ''
              serviceThinkContent.value = ''
              timeCount.value = timeCount.value + 1
              isAnswering.value = false
              isStopText.value = false
              isTyping.value = false
              nextTick(() => {
                  copyCode('.code-block-header__copy')
              })
              saveMessage()
              closeScroll.value = false;
              clearFileText();
          },
          onerror(e){
              if (apiModuleId.value == CONSTATNS.MODELS.REASONER_MODULE) {
                allText[timeCount.value].thinkContent!.status = '2'
              }
              isAnswering.value = false;
              allText[timeCount.value].adminContent.isShow = false
              allText[timeCount.value].adminContent.adminText = CONSTATNS.MESSAGES.SERVER_ERROR
              timeCount.value = timeCount.value + 1
              responce = ''
              throw showMessage(e, 'error')
          }
      });
    } else if (currentModel.value == CONSTATNS.MODELS.SPARK_ULTRA) {
        let messagesContent = ""
        if (akiText.value && isAskText.value) {
            messagesContent = akiText.value + '\n\n' + value
            isAskText.value = false
            akiText.value = ''
        } else {
            messagesContent = value
        }
        allText.push({userText: value, adminContent: {adminText: "",isShow: true} })
        let params =  {
            "max_tokens": 4096,
            "top_k": 4,
            "temperature": 0.5,
            "messages": [
                {
                    "role": "system",
                    "content": roleContent.value
                },
                {
                    "role": "user",
                    "content": messagesContent
                }
            ],
            "model": "4.0Ultra",
            "stream": true
        }
        closeScroll.value = false;
        ctrl.value = new AbortController();
        fetchEventSource(CONSTATNS.API.CHAT_COMPLETIONS, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': 'Bearer ' + CONSTATNS.API.AUTH_TOKEN
            },
            body: JSON.stringify(params),
            signal: ctrl.value.signal,
            openWhenHidden: true,
            onmessage(ev) {
                if (ev.data == '[DONE]') {
                    markdownContent.value = ''
                    timeCount.value = timeCount.value + 1
                    isAnswering.value = false
                    isStopText.value = false
                    isTyping.value = false
                    if (currentIndex.value == 0) {
                      return
                    }
                    saveMessage()
                    closeScroll.value = false;
                    clearFileText();
                } else {
                    let res = JSON.parse(ev.data)
                    markdownContent.value += res.choices[0].delta.content
                    allText[timeCount.value].adminContent.isShow = false
                    allText[timeCount.value].adminContent.adminText = getMdiText(markdownContent.value)
                    isStopText.value = true
                    lastScrollPosition.value = 0;
                }
            },
            onclose() {
                
            },
            onerror(e){
                isAnswering.value = false;
                throw showMessage(e, 'error')
            }
        });
    }
}

// 监听聊天内容变化,实时改变聊天内容高度
watch([allText], () => {
    nextTick(()=> {
        if (chatContainer.value && !closeScroll.value) {
          chatContainer.value.scrollTop = chatContainer.value.scrollHeight
        }
    })
},{
    deep: true
})

watch(contentText, (newVal) => {
    if (newVal) {
        isDisabled.value = false;
    } else {
        isDisabled.value = true;
    }
})

// 点击左侧大模型分类获取对话信息
const selectMessage = <T extends categories>(item: T) => {
    // replacement()
    currentModel.value = CONSTATNS.MODELS.SPARK_ULTRA;
    if (currentModel.value == CONSTATNS.MODELS.SPARK_ULTRA) {
        isDeepThinking.value = false;
        apiModuleId.value = CONSTATNS.MODELS.DEFAULT_MODULE;
    }
    isGenerate.value = false
    imgCurrent.value = ''
    let { name, my_name, category_img, category_id, introduce } = item
    revealAlways.value.revealName = my_name
    revealAlways.value.revealImg = category_img
    revealAlways.value.revealId = category_id
    currentIndex.value = category_id
    roleContent.value = name + introduce
    closeScroll.value = false
    lastScrollPosition.value = 0
    onceQueryDialogue(category_id, 0)
}

// 停止sse输出
const  stopExport = () => {
    if (ctrl.value) {
        ctrl.value.abort()
    }
    if (apiModuleId.value == CONSTATNS.MODELS.REASONER_MODULE) {
        if (allText[timeCount.value].thinkContent!.status == '1') {
            allText[timeCount.value].thinkContent!.status = '2'
            thinkStatus(allText[timeCount.value])
        }
    }
    isStopText.value = false
    isAnswering.value = false
    isTyping.value = false
    markdownContent.value = ''
    serviceThinkContent.value = ''
    timeCount.value = timeCount.value + 1
    nextTick(() => {
        copyCode('.code-block-header__copy')
    })
    saveMessage()
    closeScroll.value = false;
    clearFileText();
}

// 清除历史消息
function clear () {
    if (isAnswering.value) {
        showMessage(CONSTATNS.MESSAGES.CLEAR_DISABLED, 'warning')
        return
    }
    if (currentModel.value == CONSTATNS.MODELS.SPARK_ULTRA) {
        if (allText.length) {
            aicgApi.deleteDialogue({category_id: currentIndex.value, user_id: nickAll.nicknameId})
                .then(res => {
                    if (res.data.code == 0) {
                        allText.splice(0, allText.length)
                        isAnswering.value = false
                        timeCount.value = 0
                        recordLength.value = 0
                        showMessage(res.data.msg, 'success')
                    } else {
                        showMessage(res.data.msg, 'warning')
                    }
                })
        } else {
            showMessage(CONSTATNS.MESSAGES.NO_MESSAGES, 'warning')
        }
    } else {
            allText.splice(0, allText.length)
            timeCount.value = 0
            isAnswering.value = false
    }
}

// 点击页数
const handleCurrentChange = (val: number) => {
    queryPage(val, pageSize.value, promptValue.value)
}

// 查询分类标签下的信息
const optforPrompt = (item: promptObject, index: number) => {
    promptIndex.value = index
    promptValue.value = item.id
    page.value = 1
    queryPage(page.value, pageSize.value, promptValue.value)
}

// 查询类别的大模型
const queryPage = (currentPage: number, pageSize: number, template_id: number) => {
    aicgApi.queryTemplate({page: currentPage, pageSize: pageSize, template_id: template_id})
        .then(res => {
            if (res.data.code == 0) {
                templateList.splice(0, templateList.length)
                templateList.push(...res.data.data.data)
                total.value = res.data.data.total
            } else {
                showMessage(res.data.msg, 'error')
            }
        })
}

// 新增聊天模型
const cite = (item: templateObject) => {
    let temp = {
        self_introduction: item.info,
        my_name: item.name,
        name: item.characterSettings,
        introduce: item.instructionDescription,
        user_id: nickAll.nicknameId
    }
    aicgApi.addCategory(temp)
        .then(res => {
            if (res.data.code == 0) {
                showMessage(res.data.msg, 'success')
                getClassInformation(nickAll.nicknameId)
            } else {
                showMessage(res.data.msg, 'error')
            }
        })
}

// 点击查询分类
const addClassify =  () => {
    dialogTableVisible.value = true;
    aicgApi.queryPrompt()
        .then(res => {
            if (res.data.code == 0) {
                promptList.splice(0, promptList.length)
                promptList.push(...res.data.data)
                queryPage(page.value, pageSize.value, promptValue.value)
            } else {
                showMessage(res.data.msg, 'error')
            }
        })
}

// 删除大模型分类
const removeClassify = (item: menuObject) => {
    if (item.label == '删除') {
      aicgApi.deleteCategory({category_id: item.id})
        .then(res => {
            if (res.data.code == 0) {
                if (item.id == revealAlways.value.revealId) {
                    revealAlways.value = {
                        revealId: 0,
                        revealName: CONSTATNS.DEFAULT.AI_NAME,
                        revealImg: CONSTATNS.DEFAULT.AI_AVATAR
                    }
                    roleContent.value = ''
                    timeCount.value = 0
                    currentIndex.value = 0
                    allText.splice(0, allText.length)
                }
                showMessage(CONSTATNS.MESSAGES.DELETE_SUCCESS, 'success')
                getClassInformation(nickAll.nicknameId)
            } else {
                showMessage(res.data.msg, 'error')
            }
        })
    }
}

const setHoveredIndex = <T extends number | null>(index: T) => {
    hoveredIndex.value = index
}

// 点击编辑
const editChat = (index: number, text: string) => {
    editIndex.value = index
    editText.value = text
    hoveredIndex.value = null;
}

// 取消编辑
const editCancle = () => {
    editIndex.value = -1
}

//  提交修改的用户内容
const editSubmit = () => {
    editIndex.value = -1
    sendTxt(editText.value)
}

// 复制用户内容
const copyDocument = () => {
    copyCode('.copyDocument')
}

const toggleDeepThinking = () => {
    if (currentModel.value == CONSTATNS.MODELS.DEEPSEEK_V3) {
        isDeepThinking.value = !isDeepThinking.value;
        apiModuleId.value = isDeepThinking.value ? CONSTATNS.MODELS.REASONER_MODULE : CONSTATNS.MODELS.DEFAULT_MODULE;
        return
    }
    showMessage(CONSTATNS.MESSAGES.REASONER_ONLY, 'warning')
}

const thinkStatus = (item: any) => {
    if (item.thinkContent.status == '1') {
        return '思考中...'
    } else if (item.thinkContent.status == '2') {
        return '思考已停止'
    } else if (item.thinkContent.status == '3') {
        return '已思考完毕'
    }
}

const goTocustomerService = () => {
    router.push({ name: 'chatwidget', query: {appid: nickAll.appid}})
}

const saveUserProfile = () => {
    if (!editAll.value.nickname) {
        showMessage('昵称不能为空', 'error')
        return
    }
    userApi.updateUser({
        username: editAll.value.username,
        nickname: editAll.value.nickname,
        img_url: editAll.value.img_url
    }).then(res => {
        if (res.data.code == 0) {
            showMessage('个人信息已保存', 'success')
            showUserProfile.value = false
            getUser()
        } else {
            showMessage(res.data.msg, 'error')
        }
    })
}

// 关闭个人信息弹窗
const closeUserProfile = () => {
    editAll.value = {
        img_url: nickAll.nickImg,
        nickname: nickAll.nickname,
        username: nickAll.userNickname
    }
    showUserProfile.value = false
}

const openFile = () => {
    parseFile.value?.click()
}

// 上传文件
const parseFileInput = (event: Event) => {
    const target = event.target as HTMLInputElement;
    if (target.files && target.files.length > 0) {
        const file = target.files[0];
        if (!(file.type == 'application/pdf')) {
            showMessage('请上传PDF文件', 'error');
            clearFileText();
            return
        }
        const fileType = file.type.split('/').pop();
        filenameAll.value = {
            name: file.name,
            size: (file.size / 1024 / 1024).toFixed(2),
            type: fileType || 'unknown',
            isFileContent: true,
            status: '1',
            url: ''
        }
        const formdata = new FormData();
        formdata.append('avatar', file);
        aicgApi.uploadFile(formdata)
         .then(res=> {
            if (res.data.code == 0) {
                const result = res.data.data;
                filenameAll.value.status = '2';
                filenameAll.value.url = result.url;
                wordOrpdfText.value = result.text;
            } else {
                filenameAll.value.status = '3';
                showMessage(res.data.msg, 'error');
            }
         })
         .catch( () => {
            filenameAll.value.status = '3';
         })
    }
}

// 初始值
const clearFileText = () => {
    filenameAll.value = {
        isFileContent: false,
        name: '',
        size: '',
        type: '',
        status: '1',
        url: ''
    }
    wordOrpdfText.value = ''
    parseFile.value!.value = ''
}

const openUrl = () => {
    if (filenameAll.value.url) {
        window.open(filenameAll.value.url, '_blank')
    }
}
</script>

<template>
  <div class="min-h-screen bg-cover bg-center">
    <!-- 左侧对话列表 -->
    <div class="fixed left-0 top-0 h-screen w-64 bg-white/90 backdrop-blur-sm shadow-lg flex flex-col">
      <div class="p-4 border-b border-gray-200">
        <div class="grid grid-cols-4 gap-2 mb-3">
          <div
            class="aspect-square rounded-lg bg-gradient-to-br from-indigo-100 to-purple-100 flex items-center justify-center hover:shadow-md transition-shadow cursor-pointer">
            <i class="fas fa-dragon text-indigo-600 text-xl"></i>
          </div>
          <div
            class="aspect-square rounded-lg bg-gradient-to-br from-indigo-100 to-purple-100 flex items-center justify-center hover:shadow-md transition-shadow cursor-pointer">
            <i class="fas fa-fort-awesome text-indigo-600 text-xl"></i>
          </div>
          <div
            class="aspect-square rounded-lg bg-gradient-to-br from-indigo-100 to-purple-100 flex items-center justify-center hover:shadow-md transition-shadow cursor-pointer">
            <i class="fas fa-wand-sparkles text-indigo-600 text-xl"></i>
          </div>
          <div
            class="aspect-square rounded-lg bg-gradient-to-br from-indigo-100 to-purple-100 flex items-center justify-center hover:shadow-md transition-shadow cursor-pointer">
            <i class="fas fa-scroll text-indigo-600 text-xl"></i>
          </div>
        </div>
        <button
          class="w-full rounded-sm py-2 bg-gradient-to-r from-indigo-600 to-purple-600 text-white hover:opacity-90 flex items-center justify-center gap-2 cursor-pointer"
          @click="addClassify">
          <i class="fas fa-plus"></i>
          新建对话
        </button>
        <button @click="goTocustomerService" class="w-full rounded-sm py-2 mt-2 bg-gradient-to-r from-purple-500 to-pink-500 text-white hover:opacity-90 flex items-center justify-center gap-2 text-sm cursor-pointer">
            <i class="fas fa-headset"></i>
            客服应用的配置与嵌入
        </button>
      </div>
      <div class="flex-1 overflow-y-auto space-y-2 new-dialogue">
        <div class="p-2" v-for="item in categoryData" :key="item.category_id" @click="selectMessage(item)">
          <ContextMenu :menu="[
            { label: '删除', id: item.category_id}
            ]" @select="removeClassify">
            <div class="header effect hover:bg-gray-200 cursor-pointer" :class="currentIndex == item.category_id ? 'bg-gray-200':''">
                    <div class="list-icon">
                        <img class="profile" :src="item.category_img" alt="">
                    </div>
                    <div class="header-right text-gray-700">
                        <p class="name">{{item.my_name}}</p>
                        <p class="restrict" :title="item.self_introduction">{{item.self_introduction}}</p>
                    </div>
            </div>
            </ContextMenu>
        </div>
      </div>
    </div>
    <!-- 主内容区 -->
    <div class="ml-64">
      <!-- 顶部导航 -->
      <nav class="bg-white/80 backdrop-blur-sm shadow-lg px-6 py-3 flex justify-between items-center">
        <div class="flex items-center gap-4">
          <div class="flex items-center gap-2">
            <i class="fas fa-robot text-2xl text-indigo-600"></i>
            <h1 class="text-xl font-bold bg-gradient-to-r from-indigo-600 to-purple-600 bg-clip-text text-transparent">
              {{revealAlways.revealName}}</h1>
          </div>
          <div class="relative group">
            <button @click="showModelMenu = !showModelMenu"
              class="flex items-center gap-2 px-4 py-2 bg-gray-100 hover:bg-gray-200 rounded-lg text-sm whitespace-nowrap transition-colors border border-gray-200 cursor-pointer">
              <i class="fas fa-microchip text-indigo-600 mr-1"></i>
              <span class="text-indigo-600">{{ aiModels.find(m => m.id === currentModel)!.name }}</span>
              <i class="fas fa-chevron-down text-gray-400"></i>
            </button>
            <div v-if="showModelMenu"
              class="absolute left-0 mt-2 w-48 bg-white rounded-lg shadow-lg py-2 z-10 border border-gray-100">
              <div class="px-4 py-2 text-xs text-gray-500 border-b border-gray-100">选择不同的 AI 模型来应对不同场景</div>
              <button v-for="model in aiModels" :key="model.id" @click="switchModel(model.id)"
                class="w-full text-left px-4 py-2 hover:bg-gray-100 text-sm flex items-center gap-2 cursor-pointer"
                :class="{ 'text-indigo-600 bg-indigo-50': currentModel === model.id }">
                <i class="fas fa-check text-xs" :class="{ 'opacity-0': currentModel !== model.id }"></i>
                {{ model.name }}
              </button>
            </div>
          </div>
        </div>
        <div class="relative">
          <button @click="showUserMenu = !showUserMenu"
            class="flex items-center gap-2 hover:bg-gray-100 rounded-full px-3 py-2 cursor-pointer">
            <img :src="nickAll.nickImg" class="w-8 h-8 rounded-full object-cover" alt="用户头像" />
            <span class="text-gray-700">{{nickAll.nickname}}</span>
            <i class="fas fa-chevron-down text-gray-400 text-sm"></i>
          </button>
          <div v-if="showUserMenu" class="absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-lg py-2">
            <a href="#" class="block px-4 py-2 hover:bg-gray-100" @click.prevent="showUserProfile = true">
              <i class="fas fa-user mr-2 text-gray-500"></i>个人信息
            </a>
            <!-- <a href="#" class="block px-4 py-2 hover:bg-gray-100">
              <i class="fas fa-cog mr-2 text-gray-500"></i>设置
            </a> -->
            <hr class="my-2 border-gray-200" />
            <a @click="outLogin" class="block px-4 py-2 hover:bg-gray-100 text-red-500 cursor-pointer">
              <i class="fas fa-sign-out-alt mr-2"></i>退出登录
            </a>
          </div>
        </div>
      </nav>
      <!-- 个人信息弹窗 -->
    <div v-if="showUserProfile" class="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50">
        <div class="bg-white rounded-2xl w-[420px] p-8 shadow-xl">
            <div class="flex justify-between items-center mb-6">
            <h3 class="text-xl font-semibold text-gray-900">个人信息</h3>
            <button @click="closeUserProfile" class="text-gray-400 hover:text-gray-500 w-8 h-8 flex items-center justify-center rounded-full hover:bg-gray-100 cursor-pointer">
                <i class="fas fa-times"></i>
            </button>
            </div>
            <div class="flex flex-col items-center">
            <div class="relative group mb-6 imgEdit" @click="selectFile">
                <img :src="editAll.img_url" class="w-24 h-24 rounded-full object-cover" alt="用户头像"/>
                <input type="file" ref="fileInput" @change="upImg">
                <div class="absolute inset-0 bg-black/40 rounded-full opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center cursor-pointer">
                    <i class="fas fa-camera text-white text-xl"></i>
                </div>
            </div>
            <div class="w-full space-y-4">
                <div>
                    <label class="block text-sm font-medium text-gray-500 mb-1.5">昵称</label>
                    <input type="text" v-model="editAll.nickname" class="w-full px-4 py-2.5 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 name-border"/>
                </div>
                <div>
                    <label class="block text-sm font-medium text-gray-500 mb-1.5">冒险者账号</label>
                    <div class="px-4 py-2.5 bg-gray-50 rounded-lg text-gray-600">{{ nickAll.userNickname }}</div>
                </div>
            </div>
            <div class="flex gap-3 mt-8 w-full">
                <button @click="closeUserProfile" class="flex-1 py-2.5 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 !rounded-button cursor-pointer">
                取消
                </button>
                <button @click="saveUserProfile" class="flex-1 py-2.5 bg-gradient-to-r from-indigo-600 to-purple-600 text-white rounded-lg hover:opacity-90 !rounded-button cursor-pointer">
                保存
                </button>
            </div>
            </div>
        </div>
    </div>
      <!-- 聊天区域 -->
      <div class="max-w-4xl mx-auto px-4 py-6 h-[calc(100vh-180px)] flex flex-col">
        <div class="flex-1 overflow-y-auto space-y-6 pr-4 pb-4 chat-wrapper" ref="chatContainer" :class="isStopText ? 'wrapper' : ''">
          <template v-for="(item, index) in allText" :key="index">
            <div class="flex justify-end relative" v-if="editIndex == index">
                <!-- <p class="text-xs text-gray-500 mt-1" :class="{ 'text-right': message.isUser }">
                  {{ message.time }}
                </p> -->
              <div class="chat-edit">
                    <textarea name="content" class="chat-edit-textarea text-sm" v-model="editText"></textarea>
                    <div class="chat-edit-execute">
                        <button
                            class="hover:opacity-80 rounded-xl px-3 py-1 bg-gray-200 text-gray-700 text-sm cursor-pointer"
                            @click="editCancle"
                        >
                            取消
                        </button>
                        <button 
                            :disabled="!editText || isAnswering"
                            :class="editText && !isAnswering ? 'from-indigo-600 to-purple-600 hover:opacity-90': 'area-disabled'"
                            class="ml-2 rounded-xl px-3 py-1 bg-gradient-to-r from-indigo-200 to-purple-200 text-white text-sm cursor-pointer"
                            @click="editSubmit"
                        >
                            发送
                        </button>
                    </div>
                </div>
                <div class="flex gap-2 flex-row-reverse">
                  <img :src="nickAll.nickImg" class="w-10 h-10 rounded-full object-cover"
                    :alt="'用户头像'" />
                </div>
            </div>

            <div class="flex justify-end"
                v-else
                @mouseenter="setHoveredIndex(index)" 
                @mouseleave="setHoveredIndex(null)"
            >
              <div class="order-2 max-w-[70%] relative">
                <div class="flex gap-2 flex-row-reverse">
                  <img :src="nickAll.nickImg" class="w-10 h-10 rounded-full object-cover"
                    :alt="'用户头像'" />
                  <div class="px-4 py-3 rounded-2xl shadow-sm bg-indigo-500 text-white">
                    <p class="text-sm text-word">{{ item.userText }}</p>
                    <div :id="'documentText' + index" style="position: absolute; top: -9999px; left: -9999px; z-index: -9999;">{{item.userText}}</div>
                  </div>
                </div>
                <div class="group-util" v-show="hoveredIndex == index">
                    <el-tooltip
                        class="box-item"
                        effect="dark"
                        content="编辑消息"
                        placement="top"
                    >
                        <el-icon class="edit" size="20" @click="editChat(index, item.userText)"><EditPen /></el-icon>
                    </el-tooltip>
                    <el-tooltip
                        class="box-item"
                        effect="dark"
                        content="复制"
                        placement="top"
                    >
                        <el-icon class="copyDocument" size="20" @click="copyDocument" :data-clipboard-target="'#documentText' + index"><CopyDocument /></el-icon>
                    </el-tooltip>
                </div>
              </div>
            </div>

            <div class="flex justify-start">
              <div class="order-1 max-w-[100%]">
                <div class="flex gap-2">
                  <img :src="aiAvatarUrl" class="w-10 h-10 rounded-full object-cover"
                    :alt="'ai头像'" />
                    <div class="px-4 py-3 rounded-2xl shadow-lg bg-white text-gray-800 max-w-[100%]">
                        <div v-if="item.thinkContent && item.thinkContent.isShow">
                            <div @click="item.thinkContent.isOpen = !item.thinkContent.isOpen" class="think rounded-lg px-4 py-2 bg-gray-100 hover:bg-gray-200 text-black flex items-center gap-2 mr-2 cursor-pointer text-sm">
                                <i class="fas fa-lightbulb text-sm"></i>
                                {{ thinkStatus(item) }}
                                <i class="fas text-sm" :class="item.thinkContent.isOpen ? 'fa-angle-up' : 'fa-angle-down'"></i>
                            </div>
                            <div class="think-txt px-3" v-show="item.thinkContent.isOpen">
                                <div class="line"></div>
                                <div class="text-sm leading-7 ai-txt text-gray-400" v-html="item.thinkContent.thinkText"></div>
                            </div>
                        </div>
                        <div class="load" :class="item.thinkContent ? 'active' : ''" v-if="item.adminContent.isShow">
                            <div class="loading"></div>
                            <p v-if="!item.thinkContent">回答中...</p>
                        </div>
                        <div class="text-sm leading-7 ai-txt" v-else v-html="item.adminContent.adminText"></div>
                    </div>
                </div>
                <!-- <p class="text-xs text-gray-500 mt-1" :class="{ 'text-right': message.isUser }">
                  {{ message.time }}
                </p> -->
              </div>
            </div>
          </template>
        </div>
        <!-- 输入区域 -->
        <div class="entying mt-4 bg-white/80 backdrop-blur-sm rounded-xl p-4">
            <div class="space-y-2" :class="filenameAll.isFileContent ? 'pb-6 border-b border-dashed border-gray-400' : ''">
                <div v-if="filenameAll.isFileContent"
                    class="flex items-center justify-between bg-gray-200 rounded-lg px-4 py-2 w-3xs cursor-pointer transition duration-300 ease-in-out hover:shadow-xl" @click="openUrl">
                    <div class="flex items-center gap-3">
                        <div class="loading"  v-if="filenameAll.status == '1'"></div>
                        <i class="fas fa-file text-yellow-500" v-if="filenameAll.status == '2'"></i>
                        <i class="fas fa-exclamation-circle text-red-500" v-if="filenameAll.status == '3'"></i>
                        <div>
                            <div class="text-sm font-medium text-gray-700 truncate w-43">{{ filenameAll.name }}</div>
                            <div class="text-sm text-gray-500" v-if="filenameAll.status == '1'">上传中</div>
                            <div class="text-sm text-red-500" v-if="filenameAll.status == '3'">上传失败</div>
                            <div class="text-xs text-gray-500" v-if="filenameAll.status == '2'">{{ filenameAll.size }}MB · {{ filenameAll.type }}</div>
                        </div>
                    </div>
                    <button @click="clearFileText" class="text-gray-400 hover:text-red-500 cursor-pointer">
                        <i class="fas fa-times"></i>
                    </button>
                </div>
            </div>
          <div class="flex items-center gap-2" :class="filenameAll.isFileContent ? 'mt-3' : ''">
            <input type="file" ref="parseFile" @change="parseFileInput" style="display: none;">
            <el-tooltip
                class="box-item"
                effect="dark"
                content="上传文件"
                placement="top"
            >
                <button class="p-2 hover:bg-gray-100 rounded-full cursor-pointer" @click="openFile">
                    <i class="fas fa-paperclip text-gray-500"></i>
                </button>
            </el-tooltip>
            <el-tooltip
                class="box-item"
                effect="dark"
                content="清除历史记录"
                placement="top"
            >
                <button class="p-2 hover:bg-gray-100 rounded-full cursor-pointer" @click="clear">
                    <i class="fas fa-trash-alt text-gray-500" title="清除历史记录"></i>
                </button>
            </el-tooltip>
            <!-- <button class="p-2 hover:bg-gray-100 rounded-full cursor-pointer" v-if="isStopText" @click="stopExport">
              <i class="fas fa-stop text-red-500" title="停止生成"></i>
            </button> -->
            <textarea 
                v-model="contentText" 
                @keydown.enter.prevent="handleKeydown" 
                @input="adjustTextareaHeight" 
                rows="1"
                class="textarea-dialogue flex-1 resize-none border-none focus:ring-0 rounded-lg px-4 py-2 text-sm" 
                placeholder="输入你的问题..."
                ref="textareaDialogue"
                >
            </textarea>
            <button @click="toggleDeepThinking" class="rounded-sm px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 flex items-center gap-2 mr-2 cursor-pointer text-sm" :class="{'bg-indigo-100 text-indigo-700': isDeepThinking}">
                <i class="fas fa-lightbulb text-sm"></i>
                深度思考
            </button>
            <button class="rounded-3xl px-4 py-2 bg-gradient-to-r from-indigo-600 to-purple-600 text-white cursor-pointer" v-if="isStopText" @click="stopExport">
                <i class="fas fa-stop text-sm" title="停止生成"></i>
            </button>
            <button @click.stop="sendTxt(contentText)"
                v-else
                :disabled="isDisabled"
                :class="contentText ? 'from-indigo-600 to-purple-600 hover:opacity-90': 'area-disabled'"
                class="rounded-3xl px-4 py-2 bg-gradient-to-r from-indigo-200 to-purple-200 text-white cursor-pointer">
                <i class="fas fa-arrow-up text-sm"></i>
            </button>
          </div>
        </div>
      </div>
    </div>
    <el-dialog class="template-list-dialog" v-model="dialogTableVisible" title="副本选择">
        <p class="subtitle">基于冒险者实力</p>
        <ul class="template-category">
            <li class="bg-gray-600 hover:bg-indigo-600" v-for="(item, index) in promptList" :key="item.id" @click="optforPrompt(item, index)" :class="promptIndex == index ? 'bg-indigo-600' : ''">{{ item.name }}</li>
        </ul>
        <ul class="prompt">
            <li v-for="item in templateList" :key="item.id">
                <div class="detail">
                    <div class="detail-icon">⚔️</div>
                    <div class="detail-content">
                        <p><strong class="detail-title">{{ item.name }}</strong></p>
                        <p class="into" :title="item.info">{{ item.info }}</p>
                    </div>
                </div>
                <div class="action-use bg-gradient-to-r from-indigo-600 to-purple-600 text-white hover:opacity-90 cursor-pointer" @click="cite(item)">创建副本</div>
            </li>
        </ul>
        <el-pagination
            v-model:current-page="page"
            v-model:page-size="pageSize"
            :disabled="disabled"
            layout="prev, pager, next, jumper"
            :total="total"
            @current-change="handleCurrentChange"
        />
    </el-dialog>
  </div>
</template>

<style lang="scss" scoped>
.load {
    display: flex;
    border-radius: 13px;
    letter-spacing: .5px;
    line-height: 24px;
}

.load.active {
    margin: 10px 0 0 10px;
}

.load p {
    font-size: 14px;
    color: #000;
    margin-left: 5px;
}

.loading {
    animation: api-debug_load__hsrQ8 2s linear infinite;
    border-radius: 10px;
    border-right: 2px solid #87a2f1;
    border-top: 2px solid #87a2f1;
    content: "";
    display: block;
    height: 20px;
    width: 20px;
}

@keyframes api-debug_load__hsrQ8 {
    0% {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
    }
    50% {
        -webkit-transform: rotate(180deg);
        transform: rotate(180deg);
    }
    100% {
        -webkit-transform: rotate(1turn);
        transform: rotate(1turn);
    }
}

:deep(.pre-code-box) {
    margin-top: 10px;
}

:deep(.pre-code-header) {
    align-items: center;
    background: #343541;
    color: #ddd;
    display: flex;
    font-size: 12px;
    height: 40px;
    justify-content: space-between;
    line-height: 40px;
    padding: 0 1rem;
    border-top-left-radius: 5px;
    border-top-right-radius: 5px;
}

:deep(.code-block-header__lang) {
    font-size: 14px;
    font-weight: bold;
    color: #fff;
}

:deep(.code-block-header__actions) {
    display: flex;
    gap: 15px;
}

:deep(.code-block-header__copy),
:deep(.code-block-header__download) {
    cursor: pointer;
    padding: 0 8px;
    border-radius: 4px;
    transition: all 0.3s;
    color: #fff;
    font-size: 13px;
    display: flex;
    align-items: center;
    gap: 4px;
}

:deep(.code-block-header__copy:hover),
:deep(.code-block-header__download:hover) {
    background: rgba(255, 255, 255, 0.1);
}

:deep(.code-block-header__copy::before) {
    content: "📋";
    font-size: 14px;
}

:deep(.code-block-header__download::before) {
    content: "⬇️";
    font-size: 14px;
}

:deep(.pre-code code.hljs) {
    border-bottom-left-radius: 5px;
    border-bottom-right-radius: 5px;
    padding: 15px;
    font-size: 14px;
    line-height: 1.6;
    overflow-wrap: break-word;
    white-space: pre-wrap;
    word-break: normal;
}

:deep(.ai_detail table) {
    border-collapse: collapse;
}

:deep(.ai_detail th) {
    border: 1px solid #a8a7a7;
    background-color: rgb(76, 61, 111);
    padding: 5px;
}

:deep(.ai_detail td) {
    border: 1px solid #a8a7a7;
    padding: 10px;
}

:deep(.ai_detail a) {
    text-decoration: none;
    cursor: auto;
    color: #fff;
}

:deep(.ai-txt p) {
  margin-bottom: 10px !important;
}

:deep(.ai-txt p:last-child) {
  margin-bottom: 0 !important;
}

:deep(.ai-txt table) {
    border-collapse: collapse;
}

:deep(.ai-txt th) {
    border: 1px solid #a8a7a7;
    padding: 5px;
}

:deep(.ai-txt td) {
    border: 1px solid #a8a7a7;
    padding: 10px;
}

:deep(.ai-txt a) {
    text-decoration: none;
    cursor: auto;
}

.wrapper {
  scroll-behavior: smooth;
}

/* 修改垂直滚动条 */
.chat-wrapper::-webkit-scrollbar, .chat-edit-textarea::-webkit-scrollbar,.new-dialogue::-webkit-scrollbar {
  width: 5px; /* 修改宽度 */
}
 
/* 修改滚动条滑块颜色 */
.chat-wrapper::-webkit-scrollbar-thumb, .chat-edit-textarea::-webkit-scrollbar-thumb,.new-dialogue::-webkit-scrollbar-thumb {
  background-color: transparent;
  border-radius: 5px;
  transition: all 1s;
}

.chat-wrapper:hover::-webkit-scrollbar-thumb, .chat-edit-textarea:hover::-webkit-scrollbar-thumb, .new-dialogue:hover::-webkit-scrollbar-thumb{
  background-color: rgb(172, 169, 169);
}

.list-icon {
    position: relative;
    width: 30px;
    height: 30x;
}

.restrict {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    width: 185px;
}

.header {
    font-size: 14px;
    color: #fff;
    display: flex;
    align-items: center;
    padding-bottom: 14px;
    user-select: none;
}

.header .profile {
    width: 30px;
    height: 30px;
    border-radius: 50%;
}

.header.effect {
    padding-bottom: 0;
    padding: 10px 5px;
    border-radius: 10px;
    transition: all 1s;
    margin-top: 20px;
}

.header.effect:first-child {
  margin-top: 0;
}

.header.effect.active {
    background-color: #5c5a5a;
}

.header-right {
  margin-left: 10px;
}

:deep(.template-list-dialog) {
    width: 900px;
    background-color: #fff;
    border-radius: 10px;
    box-shadow: 0 2px 8px 0 rgba(0, 0, 0, .2);
    position: relative;
}

:deep(.template-list-dialog .el-dialog__title) {
    font-weight: 600;
    color: #000;
}

.subtitle {
    font-size: 12px;
    font-weight: 400;
    color: #000;
    margin-bottom: 5px;
}

.template-category {
    padding: 0;
    list-style: none;
    display: flex;
    flex-flow: row wrap;
    column-gap: 32px;
    row-gap: 15px;
}

.template-category li {
    flex-shrink: 0;
    width: 80px;
    height: 30px;
    line-height: 30px;
    text-align: center;
    color: #43436b;
    font-size: 12px;
    border-radius: 13px;
    cursor: pointer;
    color: #fff;
}

.prompt {
    padding: 0;
    list-style: none;
    display: flex;
    flex-flow: row wrap;
    align-content: flex-start;
    column-gap: 41px;
    row-gap: 25px;
    list-style: none;
    padding: 0;
    overflow-y: auto;
    margin-top: 30px;
}

.prompt li {
    position: relative;
    width: 262px;
    box-sizing: border-box;
    padding: 10px 11px;
    border-radius: 8px;
    cursor: pointer;
    border: 2px solid #b6b1b1;
    transition: all .3s;
}

.prompt li:hover {
    border-color: oklch(0.511 0.262 276.966);
}

.prompt li.detail {
    flex: 1;
}

.detail {
    display: flex;
}

.detail-title {
    font-size: 14px;
    color: #000;
    font-weight: normal;
}

.detail-icon {
    font-size: 20px;
    margin-right: 10px;
    display: flex;
    align-items: center;
}

.detail-content .into {
    line-height: 18px;
    font-size: 12px;
    font-weight: 400;
    color: rgb(135, 139, 145);
    height: 38px;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
    text-overflow: ellipsis;
}

.action-use {
    width: 70px;
    height: 25px;
    line-height: 25px;
    font-size: 12px;
    color: #fff;
    text-align: center;
    border-radius: 4px;
    float: right;
    margin-top: 10px;
}

:deep(.el-pagination) {
    margin-top: 30px;
}

:deep(.el-pager li.is-active) {
    color: oklch(0.511 0.262 276.966);
}

:deep(.el-input__wrapper) {
    border: 2px solid oklch(0.511 0.262 276.966);
    border-radius: 5px;
    color: #000;
}

.area-disabled {
    cursor:no-drop;
}

.group-util {
    display: flex;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    right: calc(100% + 18px);
}

.edit {
    margin-right: 5px;
}

.edit, .copyDocument {
    width: 30px;
    height: 30px;
    color: #535151;
    border-radius: 5px;
    transition: all 1s;
}

.edit:hover, .copyDocument:hover {
    background-color: #dbd6d6;
    cursor: pointer;
}

.chat-edit {
    margin-right: 10px;
}

.chat-edit .chat-edit-textarea {
    width: 500px;
    border: 2px solid rgb(77, 107, 254);
    border-radius: 5px;
    padding: 8px 20px;
    height: 100px;
}

.chat-edit-execute {
    display: flex;
    justify-content: flex-end;
}

.think {
    width: fit-content;
    user-select: none;
}

.think-txt {
    position: relative;
    margin: 10px 0;
}

.think-txt .line {
    border-left: 2px solid #e5e5e5;
    height: calc(100% - 10px);
    margin-top: 5px;
    position: absolute;
    top: 0;
    left: 0;
}

.textarea-dialogue {
    overflow: hidden;
}

.entying {
    box-shadow: 0 0 10px #b6b5b5;
}

.name-border {
    border: 1px solid #dfdcdc;
}

.imgEdit input {
    display: none;
}

.text-word {
    white-space: pre-wrap;
}
</style>
